<script lang="ts" setup>
// 核心样式
import 'codemirror/lib/codemirror.css'

// 引入主题后还需要在 options 中指定主题才会生效
import 'codemirror/addon/display/fullscreen.css' // 全屏显示编辑器
import 'codemirror/addon/display/fullscreen.js' // 全屏显示编辑器
import 'codemirror/theme/cobalt.css'
import 'codemirror/theme/eclipse.css'
import 'codemirror/theme/ayu-dark.css'
import 'codemirror/theme/idea.css'
import 'codemirror/theme/solarized.css'
import 'codemirror/addon/hint/show-hint.css'
import 'codemirror/addon/hint/show-hint.js'
import 'codemirror/addon/hint/sql-hint.js'
import 'codemirror/addon/selection/active-line.js'
import 'codemirror/addon/edit/matchbrackets.js'
import 'codemirror/mode/sql/sql'
// 折叠
import 'codemirror/addon/fold/foldgutter.css'
import 'codemirror/addon/fold/foldcode'
import 'codemirror/addon/fold/foldgutter'
import 'codemirror/addon/fold/brace-fold'
import 'codemirror/addon/fold/comment-fold'
import 'codemirror/addon/fold/markdown-fold'
import 'codemirror/addon/fold/xml-fold'
import 'codemirror/addon/fold/indent-fold'

import { format } from 'sql-formatter'
import CodeMirror from 'codemirror'
import { computed, onMounted, ref, shallowRef } from 'vue'
interface Props {
  modelValue: string
  zoom: boolean
  height: string
  mode: string
}
// 入参
const props = withDefaults(defineProps<Props>(), {
  height: '300px',
  mode: 'text/x-sparksql',
  zoom: false,
  modelValue: '',
})
const emit = defineEmits(['update:modelValue'])

// 生命周期
onMounted(() => {
  init()
  setSize('auto', props.height)
})

// 中转参数
const tempModelValue = computed({
  get() {
    return props.modelValue
  },
  set(val: string) {
    emit('update:modelValue', val)
  },
})
const codeMirror = ref<any>()
const sqlEditor = shallowRef()
// 编辑器属性
const options = ref({
  lineNumbers: true,
  styleActiveLine: true,
  theme: 'eclipse',
  lineWrapping: true,
  extraKeys: 'Ctrl-Space',
  lint: true,
})
const init = () => {
  sqlEditor.value = CodeMirror.fromTextArea(codeMirror.value, {
    mode: props.mode, // 编辑器模式
    autorefresh: true,
    smartIndent: true,
    lineNumbers: options.value.lineNumbers, // 显示行号
    styleActiveLine: options.value.styleActiveLine, // 激活当前行
    theme: options.value.theme, // 主题
    lineWrapping: options.value.lineWrapping, // 自动换行
    matchBrackets: true, // 括号匹配
    fullScreen: fullFlag.value,
    extraKeys: {
      // 触发提示按键
      [options.value.extraKeys]: 'autocomplete',
    },
    hintOptions: {
      // 自定义提示选项
      completeSingle: false, // 当匹配只有一项的时候是否自动补全
      tables: {}, // 代码提示
    },
    gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'CodeMirror-lint-markers'],
    foldGutter: {
      rangeFinder: new CodeMirror.fold.combine(CodeMirror.fold.indent, CodeMirror.fold.comment),
    }, // 折叠
    showCursorWhenSelecting: true,
    indentUnit: 2,
  })
  setValue()
  editorEvents()
}
const setValue = (sql?: string) => {
  const str = sql ? sql : tempModelValue.value || ''
  // 格式化赋值
  let formatStr = ''
  if (props.mode === 'text/x-sparksql') {
    formatStr = format(str)
  } else if (props.mode === 'application/json' && str) {
    formatStr = JSON.stringify(JSON.parse(str), null, 2)
  } else {
    formatStr = str
  }
  sqlEditor.value.setValue(formatStr)
  // 解决位置错乱
  sqlEditor.value.refresh()
}
const setSize = (width: string, height: string) => {
  sqlEditor.value.setSize(width, height)
}

const editorEvents = () => {
  // 设置代码提示
  sqlEditor.value.on('keyup', (cm: any, event: any) => {
    if (event.keyCode >= 65 && event.keyCode <= 90) {
      cm.showHint()
    }
    //所有的字母和'$','{','.'在键按下之后都将触发自动完成
    if (
      !cm.state.completionActive &&
      ((event.keyCode >= 65 && event.keyCode <= 90) ||
        event.keyCode === 52 ||
        event.keyCode === 219 ||
        event.keyCode === 190)
    ) {
      const Mirror = CodeMirror as any
      Mirror.commands.autocomplete(cm, null, { completeSingle: false })
    }
  })
  // 代码输入的双向绑定
  sqlEditor.value.on('change', (editor: any) => {
    // 这里要用多一个载体去获取值,不然会重复赋值卡顿
    const code = editor.getValue()
    tempModelValue.value = code
  })
}

/**
 * 全屏
 */
const fullFlag = ref<boolean>(false)
const setfullScreen = (flag: boolean) => {
  fullFlag.value = !sqlEditor.value.options.fullScreen
  sqlEditor.value.toTextArea()
  sqlEditor.value = null
  init()
  const height = flag ? 'auto' : '300px'
  setSize('auto', height)
}

defineExpose({ setValue })
</script>
<template>
  <div class="sql-editor" :class="{ full: fullFlag }">
    <textarea ref="codeMirror"></textarea>
    <span v-if="zoom" class="full-icon" @click="setfullScreen(!fullFlag)">
      <el-icon :size="25" color="#617d98"> <rank /> </el-icon
    ></span>
  </div>
</template>
<style lang="scss" scoped>
.sql-editor {
  width: 100%;
  height: 100%;
  position: relative;
  .full-icon {
    cursor: pointer;
    position: absolute;
    top: 0;
    right: 0;
    z-index: 99;
  }
  &.full {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 99;
    .full-icon {
      top: 10px;
      right: 10px;
    }
  }
}
</style>
